
class Grid extends egret.Sprite{

	public static GAME_OVER:string = "GAME_OVER";
	public static LEVEL_UP:string = "LEVEL_UP";
	public static BOOM:string = "BOOM";
	public static CREATE_ITEM:string = "CREATE_ITEM";

	public nextItem:Item;
	public isOver:boolean;
	public curLevel:number;
	public score:number;

	data:Array<Array<number>>;
	bg:egret.Shape;
	frame:number = 0;
	speed:number = 0;

	curItem:Item;
	unitList:Array<Array<Unit>>;
	boomList:Array<number>;
	redLine:number = Number.MAX_VALUE;

	levelUpEvent:egret.Event;
	gameOverEvent:egret.Event;
	boomEvent:egret.Event;
	createItemEvent:egret.Event;
	
	public constructor(){
		super();
		this.init();
	}

	init():void{
		this.levelUpEvent = new egret.Event(Grid.LEVEL_UP);
		this.gameOverEvent = new egret.Event(Grid.GAME_OVER);
		this.boomEvent = new egret.Event(Grid.BOOM);
		this.createItemEvent = new egret.Event(Grid.CREATE_ITEM);
		this.data = [];
		this.unitList = [];
		this.boomList = [];
		this.bg = new egret.Shape();
		this.addChild(this.bg);
		this.bg.graphics.lineStyle(1,0xcccccc);
		for(let i:number = 0; i<GRID_ROWS; i++){
			this.drawLine(i);
		}
		for(let j:number = 0; j<GRID_COLUMNS; j++){
			this.drawLine(j,true);
		}
		this.drawLine(GRID_COLUMNS,true);
		this.bg.graphics.endFill();
		this.addEventListener(egret.Event.ENTER_FRAME,this.onEnterFrame,this);
		var d:Grid = this;
		this.clear();
		this.start();

		document.onkeydown = function(e:any):void{
			e = e||event;
			let key:number = e.keyCode||e.which||e.charCode;
			let keyName:string = String.fromCharCode(key);
			if(d.curItem == null){
				if(isPress(key,KEY_RESTART)){
					d.start();
				}
				return;
			}
			if(isPress(key,KEY_LEFT)){
				d.left();
			}else if(isPress(key,KEY_RIGHT)){
				d.right();
			}else if(isPress(key,KEY_CHANGE)){
				d.rotate();
			}else if(isPress(key,KEY_DOWN)){
				d.accelerate();
			}else if(isPress(key,KEY_DOWN_FAST)){
				d.immediately();
			}
		};
	}

	public clear():void{
		for(let i:number = 0; i<GRID_ROWS; i++){
			if(this.data[i] == null){
				this.data[i] = [];
			}
			if(this.unitList[i] == null){
				this.unitList[i] = [];
			}
			for(let j:number = 0; j<GRID_COLUMNS; j++){
				this.data[i][j] = 0;
				let u:Unit = this.unitList[i][j];
				if(u != null){
					u.destroy();
					this.unitList[i][j] = null;
				}
			}
		}
		this.boomList.length = 0;
		this.redLine = Number.MAX_VALUE;
		if(this.nextItem != null){
			this.nextItem.destroy();
		}
	}
	

	public start():void{
		this.isOver = false;
		this.curLevel = 1;
		this.speed = getSpeed(1);
		this.score = 0;
		this.nextItem = Item.create();
	}

	drawLine(index:number,isV:boolean = false):void{
		if(!isV){
			this.bg.graphics.moveTo(0,index * GRID_SIZE);
			this.bg.graphics.lineTo(GRID_COLUMNS * GRID_SIZE,index * GRID_SIZE);
		}else{
			this.bg.graphics.moveTo(index * GRID_SIZE,0);
			this.bg.graphics.lineTo(index * GRID_SIZE,(GRID_ROWS - 1) * GRID_SIZE);
		}
	}

	onEnterFrame(e:egret.Event):void{
		if(this.isOver){
			return;
		}
		if(this.frame % this.speed == 0){
			this.next();
		}
		this.frame ++;
	}

	next():void{
		if(this.boomList.length > 0){
			this.boom(this.boomList.shift());
			return;
		}
		if(this.curItem == null){
			this.curItem = this.nextItem;
			this.addChild(this.curItem);
			this.setPos();
			this.nextItem = Item.create();
			this.dispatchEvent(this.createItemEvent);
			return;
		}
		this.down();
	}

	checkBlock(gx:number,gy:number,d:Array<Array<number>> = null):boolean{
		if(d == null){
			d = this.curItem.data;
		}
		let w:number = d[0].length; 
		let h:number = d.length;
		if(gy + h >= GRID_ROWS){
			return true;
		}
		if(gx < 0 || gx > GRID_COLUMNS){
			return true;
		}
		for(let x:number = 0; x < w; x++){
			for(let y:number = 0; y < h; y++){
				if(d[y][x] == 0){
					continue;
				}
				let ggx = gx + x;
				let ggy = gy + y;
				if(this.data[ggy] != null && this.data[ggy][ggx] > 0){
					return true;
				}
			}
		}
		return false;
	}

	fix():void{
		let w:number = this.curItem.getWidth();
		let h:number = this.curItem.getHeight();
		let gx:number = this.curItem.gx;	
		let gy:number = this.curItem.gy;	
		for(let u of this.curItem.unitList){
			let ggx = gx + u.gx;
			let ggy = gy + u.gy;
			if(ggy < this.redLine){
				this.redLine = ggy;
			}
			if(ggy < 0){
				continue;
			}
			this.data[ggy][ggx] = 1;
			u.reset(ggx,ggy);
			this.addChild(u);
			this.unitList[ggy][ggx] = u;
		}
		this.curItem.merge();
		this.curItem = null;
		this.count();
	}

	down():void{
		let gx:number = this.curItem.gx;
		let gy:number = this.curItem.gy + 1;
		if(!this.checkBlock(gx,gy)){
			this.setPos(gx,gy);
		}else{
			this.fix();
		}
	}

	left():void{
		let gx:number = this.curItem.gx - 1;
		if(gx < 0){
			gx = 0;
		}
		let gy:number = this.curItem.gy;
		if(!this.checkBlock(gx,gy)){
			this.setPos(gx,gy);
		}
	}

	right():void{
		let gx:number = this.curItem.gx + 1;
		let gy:number = this.curItem.gy;
		if(!this.checkBlock(gx,gy)){
			this.setPos(gx,gy);
		}
	}

	rotate():void{
		let gx:number = this.curItem.gx;
		let gy:number = this.curItem.gy;
		let d:Array<Array<number>> = this.curItem.getRotateData();
		let p1:egret.Point = Item.getCOG(d);
		let p2:egret.Point = Item.getCOG(this.curItem.data);
		let ogx:number = p1.x - p2.x;
		let ogy:number = p1.y - p2.y;
		let tgx:number = gx - ogx;
		let tgy:number = gy - ogy;
		if(!this.checkBlock(tgx, tgy ,d)){
			this.curItem.rotate();
			this.setPos(tgx,tgy);
		}
	}

	accelerate():void{
		this.next();
	}

	immediately():void{
		while(this.curItem != null){
			this.next();
		}
	}

	setPos(gx:number = -1,gy:number = -99):void{
		if(this.curItem == null){
			return;
		}
		let w:number = this.curItem.getWidth();
		let h:number = this.curItem.getHeight();
		if(gx < 0){
			gx = Math.floor(Math.random() * (GRID_COLUMNS - w));
		}
		if(gx > GRID_COLUMNS - w){
			gx = GRID_COLUMNS - w;
		}
		if(gy ==  -99){
			gy = - this.curItem.getHeight();
		}
		this.curItem.x = gx * GRID_SIZE;
		this.curItem.y = gy * GRID_SIZE;
		this.curItem.gx = gx;
		this.curItem.gy = gy;
	}

	count():void{
		this.boomList.length = 0;
		for(let i:number = 0; i<GRID_ROWS; i++){
			let lineBoom:boolean = true;
			for(let j:number = 0; j<GRID_COLUMNS; j++){
				if(this.data[i][j] == 0){
					lineBoom = false;
					break;
				}
			}
			if(lineBoom){
				this.boomList.push(i);
			}
		}
		if(this.boomList.length == 0){
			if(this.redLine <= 0){
				this.gameOver();
			}
		}
	}

	boom(index:number):void{
		for(let i:number = GRID_ROWS - 1; i>= 0; i--){
			if(i == index){
				for(let j:number = 0; j<GRID_COLUMNS; j++){
					this.data[i][j] = 0;
					let u:Unit = this.unitList[i][j];
					if(u != null){
						u.destroy();
						this.unitList[i][j] = null;
					}
				}
			}else if(i < index){
				for(let j:number = 0; j<GRID_COLUMNS; j++){
					this.data[i+1][j] = this.data[i][j];
					let u:Unit = this.unitList[i][j];
					this.unitList[i+1][j] = u;
					if(u != null){
						u.reset(j,i+1);
					}
				}
			}
		}
		this.redLine ++;
		this.score += getAddScore(this.curLevel);
		if(this.score >= getMaxScore(this.curLevel)){
			this.levelUp();
		}
		this.dispatchEvent(this.boomEvent);
	}

	levelUp():void{
		this.curLevel += 1;
		this.speed = getSpeed(this.curLevel);
		this.dispatchEvent(this.levelUpEvent);
	}

	gameOver():void{
		this.isOver = true;
		this.clear();
		this.dispatchEvent(this.gameOverEvent);
	}
}
